#include <sys/stat.h>
#include <pybind11/embed.h>
#include <pybind11/stl.h>

inline int run_campaign_json_()
{
  std::unordered_map<std::string, uint8_t> campaign_status;
  // 1. std read json file
  std::ifstream file("campaigns.json");
  if (!file.is_open())
  {
    std::cerr << "run_campaign_json_(): Could not open campaigns.json" << std::endl;
    return 1;
  }
  // 2. nlohmann parse json file
  nlohmann::json camp_json;
  try
  {
    file >> camp_json;
  }
  catch (const nlohmann::json::parse_error& e)
  {
    std::cerr << "run_campaign_json_(): " << e.what() << std::endl;
    file.close();
    return 2;
  }
  file.close();
  // 3. nlohmann fill json map
  for (auto it = camp_json.begin(); it != camp_json.end(); ++it)
  {
    const std::string& campaign_name = it.key();
    const nlohmann::json& campaign_data = it.value();

    if (campaign_data.contains("go") && campaign_data["go"].is_boolean())
    {
      bool go_value = campaign_data["go"].get<bool>();
      campaign_status[campaign_name] = go_value ? 1 : 0;
    }
    else campaign_status[campaign_name] = 3; // go: missing or not boolean
  }
  // 4. print summary
  std::cout << "campaigns.json: # = " << campaign_status.size() << std::endl;
  size_t enabled_count = 0;
  size_t disabled_count = 0;
  size_t unknown_count = 0;
  for (const auto& pair : campaign_status)
  {
    if (pair.second == 1) enabled_count++;
    else if (pair.second == 3) unknown_count++;
    else disabled_count++;
  }
  std::cout << "  Enabled = " << enabled_count << "\n  Disabled = " << disabled_count << "\n  Unknown = " << unknown_count << std::endl;
  std::cout << std::string(45, '=') << std::endl;
  // 5. serialize campaign_status to file
  size_t serialized_size = cc_size_(campaign_status);
  void* buffer = malloc(serialized_size);
  if (!buffer)
  {
    std::cerr << "run_campaign_json_(): Failed to allocate buffer for serialization" << std::endl;
    return 3;
  }
  size_t actual_size = cc_serialize_(campaign_status, buffer);
  dat_t dat(buffer, actual_size);
  if (dat.new_("cc_json.dat", actual_size) != 0)
  {
    std::cerr << "run_campaign_json_(): Failed to write cc_json.dat" << std::endl;
    free(buffer);
    return 4;
  }
  free(buffer);
  std::cout << "run_campaign_json_(): Serialized campaign_status to cc_json.dat (" << actual_size << " bytes)" << std::endl;
  return 0;
}

inline int run_campaign_jira_()
{
  jira_c config;
  std::vector<jira_s> jira_issues = jira_fetch_campaign_(config);
  // 4. print summary
  std::cout << "\nJIRA issues # = " << jira_issues.size() << std::endl;
  std::cout << std::string(45, '-') << std::endl;
  // 5. print first 10 issues
  size_t print_count = std::min(jira_issues.size(), size_t(10));
  for (size_t i = 0; i < print_count; ++i)
  {
    std::cout << "Issue #" << (i + 1) << "\n\n";
    jira_issues[i].print_();
    std::cout << std::endl;
  }
  if (jira_issues.size() > 10) std::cout << "... and " << (jira_issues.size() - 10) << " more issues" << std::endl;
  // 6. serialize jira_issues to file
  size_t serialized_size = cc_size_(jira_issues);
  void* buffer = malloc(serialized_size);
  if (!buffer)
  {
    std::cerr << "run_campaign_jira_(): Failed to allocate buffer for serialization" << std::endl;
    return 1;
  }
  size_t actual_size = cc_serialize_(jira_issues, buffer);
  dat_t dat(buffer, actual_size);
  if (dat.new_("cc_jira.dat", actual_size) != 0)
  {
    std::cerr << "run_campaign_jira_(): Failed to write cc_jira.dat" << std::endl;
    free(buffer);
    return 2;
  }
  free(buffer);
  std::cout << "run_campaign_jira_(): Serialized jira_issues to cc_jira.dat (" << actual_size << " bytes)" << std::endl;
  return 0;
}

inline int run_campaign_verify_()
{
  // 1. Deserialize campaign_status from cc_json.dat
  {
    std::unordered_map<std::string, uint8_t> campaign_status;
    struct stat file_stat;
    if (stat("cc_json.dat", &file_stat) != 0)
    {
      std::cerr << "run_verify_(): Could not stat cc_json.dat" << std::endl;
      return 1;
    }
    size_t file_size = file_stat.st_size;
    void* buffer = malloc(file_size);
    if (!buffer)
    {
      std::cerr << "run_verify_(): Failed to allocate buffer for cc_json.dat" << std::endl;
      return 2;
    }
    dat_t dat(buffer, file_size);
    if (dat.get_("cc_json.dat", 0, file_size) != 0)
    {
      std::cerr << "run_verify_(): Failed to read cc_json.dat" << std::endl;
      free(buffer);
      return 3;
    }
    cc_deserialize_(campaign_status, buffer);
    free(buffer);
    // printout
    std::cout << "campaigns.json: # = " << campaign_status.size() << std::endl;
    size_t enabled_count = 0;
    size_t disabled_count = 0;
    size_t unknown_count = 0;
    for (const auto& pair : campaign_status)
    {
      if (pair.second == 1) enabled_count++;
      else if (pair.second == 3) unknown_count++;
      else disabled_count++;
    }
    std::cout << "  Enabled = " << enabled_count << "\n  Disabled = " << disabled_count << "\n  Unknown = " << unknown_count << std::endl;
    std::cout << std::string(45, '=') << std::endl;
  }
  // 2. Deserialize jira_issues from cc_jira.dat
  {
    std::vector<jira_s> jira_issues;
    struct stat file_stat;
    if (stat("cc_jira.dat", &file_stat) != 0)
    {
      std::cerr << "run_verify_(): Could not stat cc_jira.dat" << std::endl;
      return 4;
    }
    size_t file_size = file_stat.st_size;
    void* buffer = malloc(file_size);
    if (!buffer)
    {
      std::cerr << "run_verify_(): Failed to allocate buffer for cc_jira.dat" << std::endl;
      return 5;
    }
    dat_t dat(buffer, file_size);
    if (dat.get_("cc_jira.dat", 0, file_size) != 0)
    {
      std::cerr << "run_verify_(): Failed to read cc_jira.dat" << std::endl;
      free(buffer);
      return 6;
    }
    cc_deserialize_(jira_issues, buffer);
    free(buffer);
    // printout
    std::cout << "\nJIRA issues # = " << jira_issues.size() << std::endl;
    std::cout << std::string(45, '-') << std::endl;
    size_t print_count = std::min(jira_issues.size(), size_t(10));
    for (size_t i = 0; i < print_count; ++i)
    {
      std::cout << "Issue #" << (i + 1) << "\n\n";
      jira_issues[i].print_();
      std::cout << std::endl;
    }
    if (jira_issues.size() > 10) std::cout << "... and " << (jira_issues.size() - 10) << " more issues" << std::endl;
  }
  // 3. Reset and print full events to text files
  {
    // 3a. Deserialize and write campaign_status to cc_json.txt
    {
      std::unordered_map<std::string, uint8_t> campaign_status;
      struct stat file_stat;
      if (stat("cc_json.dat", &file_stat) == 0)
      {
        size_t file_size = file_stat.st_size;
        void* buffer = malloc(file_size);
        if (buffer)
        {
          dat_t dat(buffer, file_size);
          if (dat.get_("cc_json.dat", 0, file_size) == 0)
          {
            cc_deserialize_(campaign_status, buffer);
          }
          free(buffer);
        }
      }
      std::ofstream out_file("cc_json.txt");
      if (out_file.is_open())
      {
        out_file << "Campaign Status Report\n";
        out_file << std::string(80, '=') << "\n\n";
        out_file << "Total Campaigns: " << campaign_status.size() << "\n\n";
        size_t enabled_count = 0;
        size_t disabled_count = 0;
        size_t unknown_count = 0;
        for (const auto& pair : campaign_status)
        {
          if (pair.second == 1) enabled_count++;
          else if (pair.second == 3) unknown_count++;
          else disabled_count++;
        }
        out_file << "Summary:\n";
        out_file << "  Enabled:  " << enabled_count << "\n";
        out_file << "  Disabled: " << disabled_count << "\n";
        out_file << "  Unknown:  " << unknown_count << "\n";
        out_file << std::string(80, '-') << "\n\n";
        out_file << "Full Campaign List:\n\n";
        for (const auto& pair : campaign_status)
        {
          std::string status_str;
          if (pair.second == 1) status_str = "ENABLED";
          else if (pair.second == 3) status_str = "UNKNOWN";
          else status_str = "DISABLED";
          out_file << std::setw(60) << std::left << pair.first << " [" << status_str << "]\n";
        }
        out_file.close();
        std::cout << "Written full campaign_status to cc_json.txt" << std::endl;
      }
    }
    // 3b. Deserialize and write jira_issues to cc_jira.txt
    {
      std::vector<jira_s> jira_issues;
      struct stat file_stat;
      if (stat("cc_jira.dat", &file_stat) == 0)
      {
        size_t file_size = file_stat.st_size;
        void* buffer = malloc(file_size);
        if (buffer)
        {
          dat_t dat(buffer, file_size);
          if (dat.get_("cc_jira.dat", 0, file_size) == 0)
          {
            cc_deserialize_(jira_issues, buffer);
          }
          free(buffer);
        }
      }
      std::ofstream out_file("cc_jira.txt");
      if (out_file.is_open())
      {
        out_file << "JIRA Issues Report\n";
        out_file << std::string(80, '=') << "\n\n";
        out_file << "Total Issues: " << jira_issues.size() << "\n\n";
        out_file << std::string(80, '-') << "\n\n";
        for (size_t i = 0; i < jira_issues.size(); ++i)
        {
          out_file << "Issue #" << (i + 1) << "\n";
          out_file << std::string(80, '-') << "\n";
          out_file << "Key:        " << jira_issues[i].key << "\n";
          out_file << "Summary:    " << jira_issues[i].summary << "\n";
          out_file << "Status:     " << jira_issues[i].status << "\n";
          out_file << "Priority:   " << jira_issues[i].priority << "\n";
          out_file << "Updated:    " << jira_issues[i].updated << "\n";
          out_file << "Description:\n";
          if (!jira_issues[i].description.empty())
          {
            out_file << jira_issues[i].description << "\n";
          }
          else
          {
            out_file << "(No description)\n";
          }
          out_file << "\n";
        }
        out_file.close();
        std::cout << "Written full jira_issues to cc_jira.txt" << std::endl;
      }
    }
  }
  return 0;
}

inline int run_campaign_()
{
  run_campaign_json_();
  run_campaign_jira_();
  run_campaign_verify_();
  return 0;
}